package helper.wsdlHelper;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;


import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.soap.SOAPBinding;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.wsmo.common.Entity;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class DynamicInvokerSOAP {
	   /**
     * Method invokeMethod
     *
     * @param wsdlLocation
     * @param operationName
     * @param inputName
     * @param outputName
     * @param portName
     * @param args
     *
     * @return
     *
     * @throws Exception
     */
    public String invokeMethod(String wsdlURIStr, String operationName, String portName, String soapBody)
    throws Exception {
    	Document xmlModel;
    	String endPoint = "";
    	String serviceName = "";
    	String targetNS = "";
    	String soapAction = "";
    	
    	
    	DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
    	docBuilderFactory.setNamespaceAware(true);
    	DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    	

    	
    	xmlModel = docBuilder.parse(wsdlURIStr);
    	
    	NodeList nodes; 

		nodes = xmlModel.getElementsByTagName("wsdlsoap:address");
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("soap:address") : nodes;
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("address") : nodes;
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("wsdl:address") : nodes;
		endPoint = nodes.item(0).getAttributes().item(0).getNodeValue().toString();
		System.out.println(endPoint);
		
		nodes = xmlModel.getElementsByTagName("wsdl:service");
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("service") : nodes;
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("soap:service") : nodes;
		serviceName = nodes.item(0).getAttributes().item(0).getNodeValue().toString();
		System.out.println(serviceName);
		
		nodes = xmlModel.getElementsByTagName("wsdl:definitions");
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("definitions") : nodes;
		nodes = (nodes.item(0)==null) ? xmlModel.getElementsByTagName("soap:definitions") : nodes;
		targetNS = nodes.item(0).getAttributes().getNamedItem("targetNamespace").getNodeValue();
		System.out.println(targetNS);
		
		
		
		System.out.println("URI : " + wsdlURIStr + "\nporttype: "+portName + " \noperation: " + operationName + "\nendpoint: " + endPoint+"\nTNS : " + targetNS);
		
		
		QName serviceQName = new QName(targetNS, serviceName);
		Service serv = Service.create(serviceQName);
		
		QName portQName = new QName(targetNS, portName);
		serv.addPort(portQName, SOAPBinding.SOAP11HTTP_BINDING, endPoint);
		

		
		
		MessageFactory factory = MessageFactory.newInstance();
        SOAPMessage message = factory.createMessage();
       
		InputSource is = new InputSource(new StringReader(soapBody));
		org.w3c.dom.Document doc=null;
		doc = docBuilder.parse(is);
        
        message.getSOAPBody().addDocument(doc);
        
        message.getMimeHeaders().addHeader("SOAPAction", operationName);

        message.saveChanges();

        Dispatch<SOAPMessage> smDispatch = serv.createDispatch(portQName, SOAPMessage.class, Service.Mode.MESSAGE);
        System.out.println(portQName+" "+SOAPMessage.class+" "+Service.Mode.MESSAGE);
        
        List<Handler> handlers =  smDispatch.getBinding().getHandlerChain();
        handlers.add(new SOAPLoggingHandler());
        smDispatch.getBinding().setHandlerChain(handlers);   
        
        SOAPMessage soapResponse = null;
        try {
        	soapResponse = smDispatch.invoke(message);
        } catch (Exception e){
        	e.printStackTrace();
        	System.out.println("Fault has been returned in SOAP message!!!");
        }
        
    	ByteArrayOutputStream bos = new ByteArrayOutputStream();
    	soapResponse.writeTo(bos);
        String returnSOAPString = bos.toString();
        
        return returnSOAPString;
    	
    }
    
    
    public static void main(String[] args) throws Exception{
    	String result="";
    	result += "<identifyCustomer xmlns='http://crm.portal.telekomunikacja.pl/xsd'>" +"\n";
		result += " <customerLastName >"+"Laurie"+"</customerLastName>"+"\n";
		result += "</identifyCustomer>";
		
		String wsdlLocation="http://localhost:8080/axis2/services/CRMService?wsdl";
		String portType="CRMServicePortType";
		String operation="identifyCustomer";
		
		DynamicInvokerSOAP di= new DynamicInvokerSOAP();
		
		di.invokeMethod(wsdlLocation,operation,portType,result);
		
    }
    
	//utility method converting SOAPMessage to W3C dom Document
	public static Document toDocument(SOAPMessage soapMsg)
			throws ParserConfigurationException, SAXException, SOAPException,
			IOException {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		soapMsg.writeTo(outputStream);
		ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		docFactory.setNamespaceAware(true);
		DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
		Document doc = docBuilder.parse(inputStream);
		return doc;
	}
    
	public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {
		public boolean handleMessage(SOAPMessageContext smc) {
			 logToSystemOut(smc);
			 return true;
		}

		public boolean handleFault(SOAPMessageContext smc) {
			logToSystemOut(smc);
			return true;
		}

		public Set<QName> getHeaders() {
			return null;
		}

		public void close(MessageContext context) {
		}
		
	    private void logToSystemOut(SOAPMessageContext smc) {
	        boolean direction = ((Boolean) smc.get (javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY)).booleanValue();
	        String directionStr = direction ? "\n---------Outgoing SOAP message---------" : "\n---------Incoming SOAP message---------";
	        
	        SOAPMessage message = smc.getMessage();
	        
	        try {
	        	//XML pretty print
	        	Document doc = toDocument(message);
	        	ByteArrayOutputStream stream = new ByteArrayOutputStream();
	        	OutputFormat format = new OutputFormat(doc);
				format.setIndenting(true);
				XMLSerializer serializer = new XMLSerializer(stream, format);
				serializer.serialize(doc);
	            System.out.println(directionStr + "\n" + stream.toString() + "----------End of SOAP message----------\n");
	        } catch (Exception e) {
	        	System.out.println(directionStr + "\n Exception was thrown  \n----------End of SOAP message----------\n");
	        }
	    }
	
	}
    
}
